package me.yuzho.study.concurrency;


import static net.mindview.util.Print.*;
import static java.util.concurrent.TimeUnit.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;




class DelayedTask implements Runnable, Delayed {
    private static int counter = 0;
    private final int id = counter++;
    
    private final int delta;
    private final long trigger;
    
    protected static List<DelayedTask> sequence = new ArrayList<DelayedTask>();
    
    public DelayedTask(int delayInMilliseconds) {
        this.delta = delayInMilliseconds;
        trigger = System.nanoTime() + NANOSECONDS.convert(delta, MILLISECONDS); 
        sequence.add(this);
    }

    @Override
    public int compareTo(Delayed o) {
        DelayedTask that = (DelayedTask)o;
        if(this.trigger < that.trigger) return -1;
        if(this.trigger > that.trigger) return 1;
        return 0;
    }

    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(trigger - System.nanoTime(), NANOSECONDS);
    }
    
    @Override
    public String toString() {
        return String.format("[%1$-4d] Task ", delta) + id;
    }
    
    public String summary() {
        return "(" + id + ": " + delta + ")";
    }

    @Override
    public void run() {
        print(Thread.currentThread().getName() + ":" + this + " ");
    }
    
    public static class EndSentinel extends DelayedTask {

        private ExecutorService exec;
        
        public EndSentinel(int delay, ExecutorService e) {
            super(delay);
            exec = e;
        }

        @Override
        public void run() {
            for(DelayedTask pt : sequence) {
                printnb(pt.summary() + " ");
            }
            print();
            print(Thread.currentThread().getName() + ":" + this + " Calling shutdown now");
            exec.shutdownNow();
        }
        
    }
}

class DelayedTaskConsumer implements Runnable {
    
    private DelayQueue<DelayedTask> q;
    
    public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {
        this.q = q;
    }

    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                print(Thread.currentThread().getName() + " taking a delayed task");
                q.take().run(); // run the task with the current thread
            }
        } catch (InterruptedException e) {
            print("Interrupt: acceptable way to exit");
        }
        
        print("Finished DelayedTaskConsumer");
    }
    
}


/** DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象
 * 其中的对象只能在其到期时才能从队列中取走
 * @author yzhou-citrix
 *
 */
public class DelayQueueDemo {

    public static void main(String[] args) {
        Random rand = new Random(47);
        
        ExecutorService exec = Executors.newCachedThreadPool();
        DelayQueue<DelayedTask> queue = new DelayQueue<DelayedTask>();
        
        // Fill with tasks that have random deploys.
        for(int i =0; i< 20; i++) {
            queue.put(new DelayedTask(rand.nextInt(5000)));
        }
        
        // Set the stopping point
        queue.add(new DelayedTask.EndSentinel(5000, exec));
        exec.execute(new DelayedTaskConsumer(queue));
    }
}
